package org.osomit.sacct.provider.impl.spring;

import org.osomit.sacct.client.iface.AccountClient;
import org.osomit.sacct.client.impl.AccountClientImpl;
import org.osomit.sacct.connector.socket.impl.SocketConnectorProviderImpl;
import org.osomit.sacct.io.impl.JSONClientReaderWriter;
import org.osomit.sacct.handler.impl.ClientServiceHandlerImpl;
import org.osomit.sacct.session.impl.SessionContextHolderImpl;
import org.osomit.sacct.entity.ApplicationAccount;
import org.osomit.sacct.provider.iface.AccountProvider;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.FactoryBean;

import java.util.*;

/**
 * Account Proivder for the Spring Framework
 *
 * @author Jian Fang (John.Jian.Fang@gmail.com)
 *
 * Date: Jul 5, 2009
 *
 */
public class AccountProviderFactoryBean implements FactoryBean, InitializingBean, DisposableBean, AccountProvider {

	public static final String ACCOUNT_PROVIDER_NAME = "SAcct";

    private Map<String, ApplicationAccount> map;
    
    private AccountClient client;

    private String clientId;

    private String accountServerHost;

    private int accountServerPort;

    private boolean active;

    public String getAccountServerHost() {
        return accountServerHost;
    }

    public int getAccountServerPort() {
        return accountServerPort;
    }

    public String getClientId() {
        return clientId;
    }

    //Must wiring in the client Id, server host, and server port from the constructor
    public AccountProviderFactoryBean(String clientId, String accountServerHost, int accountServerPort, boolean useOTP, boolean active) {
        this.clientId = clientId;
        this.accountServerHost = accountServerHost;
        this.accountServerPort = accountServerPort;
        this.active = active;
        
        client = new AccountClientImpl();
        AccountClientImpl clientImpl = (AccountClientImpl)client;
        clientImpl.setClientId(this.clientId);
        clientImpl.setUseOTP(useOTP);
        clientImpl.setConnectorProvider(new SocketConnectorProviderImpl());
        clientImpl.setReaderWriter(new JSONClientReaderWriter());
        clientImpl.setServiceHandler(new ClientServiceHandlerImpl());
        clientImpl.setSessionContextHolder(new SessionContextHolderImpl());
        
        map = Collections.synchronizedMap(new HashMap<String, ApplicationAccount>());
    }

    public AccountProviderFactoryBean(String clientId, String accountServerHost, int accountServerPort, boolean useOTP) {
        this.clientId = clientId;
        this.accountServerHost = accountServerHost;
        this.accountServerPort = accountServerPort;
        this.active = true;

        client = new AccountClientImpl();
        AccountClientImpl clientImpl = (AccountClientImpl)client;
        clientImpl.setClientId(this.clientId);
        clientImpl.setUseOTP(useOTP);
        clientImpl.setConnectorProvider(new SocketConnectorProviderImpl());
        clientImpl.setReaderWriter(new JSONClientReaderWriter());
        clientImpl.setServiceHandler(new ClientServiceHandlerImpl());
        clientImpl.setSessionContextHolder(new SessionContextHolderImpl());

        map = Collections.synchronizedMap(new HashMap<String, ApplicationAccount>());
    }

    public AccountProviderFactoryBean(String clientId, String accountServerHost, int accountServerPort) {
        this.clientId = clientId;
        this.accountServerHost = accountServerHost;
        this.accountServerPort = accountServerPort;
        this.active = true;

        client = new AccountClientImpl();
        AccountClientImpl clientImpl = (AccountClientImpl)client;
        clientImpl.setClientId(this.clientId);
        clientImpl.setUseOTP(false);
        clientImpl.setConnectorProvider(new SocketConnectorProviderImpl());
        clientImpl.setReaderWriter(new JSONClientReaderWriter());
        clientImpl.setServiceHandler(new ClientServiceHandlerImpl());
        clientImpl.setSessionContextHolder(new SessionContextHolderImpl());

        map = Collections.synchronizedMap(new HashMap<String, ApplicationAccount>());
    }

    public Object getObject() throws Exception {
        return (AccountProvider)this;
    }

    public Class getObjectType() {
        return AccountProvider.class;
    }

    public boolean isSingleton() {
        return true;
    }

    public boolean isActive() {
        return this.active;
    }

    protected void createProvider(){
        client.connect(this.accountServerHost, this.accountServerPort);

        List<ApplicationAccount> list = client.fetchAllAccounts();
        if(list != null && list.size() > 0){
            for(ApplicationAccount account: list){
                map.put(account.getName(), account);
            }
        }
        
        client.disconnect();
    }

    public void afterPropertiesSet() throws Exception {
        if(this.active){
            this.createProvider();
        }
    }

    public void destroy() throws Exception {
        if(map != null && map.size() > 0){
            Collection<ApplicationAccount> accounts = map.values();
            for(ApplicationAccount account: accounts){
                account.setPassword(account.getPassword().replaceAll(".", " "));
            }
            map.clear();
        }
    }

    public String getAccountPassword(String accountName) {
        ApplicationAccount account = map.get(accountName);
        if(account != null)
            return account.getPassword();
        
        return null;
    }
}
